package nayasis.common.util;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;

/**
 * 파일처리 유틸리티
 *
 * @author 정화수
 *
 */
public abstract class FileUtil {

	/**
	 * 파일(또는 디렉토리) 목록을 검색한다.
	 *
	 * @param searchDir        검색할 디렉토리
	 * @param includeFile      파일을 결과목록에 포함시킬지 여부
	 * @param includeDirectory 디렉토리를 결과목록에 포함시킬지 여부
	 * @param scanDepth        검색단계 ( -1 일 경우, 무한검색 )
	 * @param matchingPattern  검색조건 (glob 식이며,미입력시 전체검색)
	 * @return 파일(또는 디렉토리) 목록
	 * @throws IOException 파일처리 오류시
	 */
	public static List<Path> getList( String searchDir, boolean includeFile, boolean includeDirectory, int scanDepth, String... matchingPattern  ) throws IOException {

        Path rootDir = Paths.get( searchDir );
        
        if( ! Files.isDirectory(rootDir)) return new ArrayList<>();
        

        FileFinder finder = new FileFinder( includeFile, includeDirectory, matchingPattern );
        
        if( scanDepth < 0 ) scanDepth = Integer.MAX_VALUE;
        
        Files.walkFileTree( rootDir, EnumSet.noneOf(FileVisitOption.class), scanDepth, finder );

		
        return finder.getFindResult();

	}
	
    /**
     * 파일을 검색하는 Visitor 클래스
     * 
     * @author nayasis
     */
    private static class FileFinder extends SimpleFileVisitor<Path> {
 
        private List<PathMatcher> matchers = new ArrayList<>();
        
        private boolean checkPattern = true;
        private boolean includeFile  = true;
        private boolean includeDir   = true;
 
        private List<Path> result = new ArrayList<>();

        /**
         * 기본 생성자
         * 
         * @param includeFile 파일 포함여부
         * @param includeDir  디렉토리 포함여부
         * @param pattern     이름을 검사할 패턴식
         */
        public FileFinder( boolean includeFile, boolean includeDir, String... pattern ) {

    		for( String singlePattern : pattern ) {
    			this.matchers.add( FileSystems.getDefault().getPathMatcher("glob:" + singlePattern) );
    		}
            
    		this.checkPattern = matchers.size() != 0;
            this.includeFile  = includeFile;
            this.includeDir   = includeDir;

        }
 
        /**
         * 파일명칭의 패턴을 검사한다.
         * 
         * @param file 검사할 파일명
         */
        private void find( Path file ) {

        	Path name = file.getFileName();
        	
        	if( name == null ) return;

        	if( ! checkPattern ) {
        		result.add( file );
        		return;
        	}

        	for( PathMatcher matcher : this.matchers ) {
        		if( matcher.matches(name) ) {
        			result.add( file );
        			return;
        		}
        	}
            
        }
 
        /* (non-Javadoc)
         * @see java.nio.file.SimpleFileVisitor#visitFile(java.lang.Object, java.nio.file.attribute.BasicFileAttributes)
         */
        @Override
        public FileVisitResult visitFile( Path file, BasicFileAttributes attrs ) {
            
        	if( this.includeFile ) {
        		find( file );
        	}
        	
            return FileVisitResult.CONTINUE;
        }
 
        /* (non-Javadoc)
         * @see java.nio.file.SimpleFileVisitor#preVisitDirectory(java.lang.Object, java.nio.file.attribute.BasicFileAttributes)
         */
        @Override
        public FileVisitResult preVisitDirectory( Path dir, BasicFileAttributes attrs ) {
            
        	if( this.includeDir ) {
        		find( dir );
        	}
        	
            return FileVisitResult.CONTINUE;
        }
 
        /* (non-Javadoc)
         * @see java.nio.file.SimpleFileVisitor#visitFileFailed(java.lang.Object, java.io.IOException)
         */
        @Override
        public FileVisitResult visitFileFailed( Path file, IOException exception ) {
            System.err.println( exception );
            return FileVisitResult.CONTINUE;
        }

        /**
         * 파일 검색결과를 얻는다.
         * 
         * @return 검색결과
         */
        public List<Path> getFindResult() {
        	return this.result;
        }
        
    }
	

    /**
     * 문자열을 파일로 생성한다.
     *
     * @param filePath 경로를 포함한 파일명
     * @param text     문자열
     * @throws IOException 파일 처리 오류시
     */
    public static void makeTextFile( String filePath, String text ) throws IOException {

        File f  = new File( filePath );

        try (
        	FileWriter     fw = new FileWriter( f );
        	BufferedWriter bw = new BufferedWriter( fw );
        		
        ) {

            bw.write( text );

        } catch ( IOException e ) {
            throw e;

        }

    }

    /**
     * 파일명에서 확장자를 제거한다.
     * 
     * @param filePath 파일명 (또는 파일경로)
     * @return 확장자가 제거된 파일명 (또는 파일경로)
     */
    public static String removeExtention( String filePath ) {
    	
    	if( filePath == null ) return filePath;
    	
    	int index = filePath.lastIndexOf( '.' );
    	
    	if( index < 0 ) return filePath;
    	
    	return filePath.substring( 0, index ); 
    	
    }
 
    /**
     * 파일명의 확장자를 가져온다.
     * 
     * @param filePath 파일명 (또는 파일경로)
     * @return 파일명의 확장자
     */
    public static String getExtention( String filePath ) {

    	if( filePath == null ) return null;
    	
    	int index = filePath.lastIndexOf( '.' );
    	
    	if( index < 0 ) return filePath;
    	
    	return filePath.substring( index + 1 ).toLowerCase(); 
    	
    }

    /**
     * 디렉토리를 만든다.
     * 
     * @param dir
     * @throws IOException 
     */
    public static void makeDirectory( String dir ) throws IOException {
    	
    	Path path = Paths.get( dir );
    	
    	if( Files.exists(path) ) return;
    	
    	Files.createDirectory( path );

    }

    public static void moveFile( String filePath, String targetDir ) throws IOException {
    	
    	Path pathFile      = Paths.get( filePath  );
    	Path pathTargetDir = Paths.get( targetDir );

    	if( ! Files.exists(pathFile) ) return;
    	
    	if( ! Files.exists(pathTargetDir) ) {
    		makeDirectory( targetDir );
    	}
    	
		Files.move( pathFile, pathTargetDir.resolve( pathFile.getFileName()) );

    }

    public static void deleteFile( String filePath ) throws IOException {

    	Path path = Paths.get( filePath );
    	
    	Files.deleteIfExists( path );

    }

}